﻿
Imports std
Imports xmlctrl
Imports xhtmlctrl
Imports webctrl
Imports managedServer
Imports lpmsengi
Imports wfileis.data

Public Module wfileis
    Public Const defaultConfigfile As String = "wfileis.ini"
    Public recentTopic As recentTopic = Nothing
    Public recentCategory As recentCategory = Nothing
    Public recentWhitelist As iplist = Nothing

    Public Sub main(ByVal args() As String)
        loosenSPMLimit(1024)
        ProHttpS.defaultConfigfile = defaultConfigfile
        recentTopic = New recentTopic()
        recentCategory = New recentCategory()

        With ProHttpS.managedRequestProcessors()
            .insert(New configDelegate())
            .insert(recentTopic)
            .insert(recentCategory)
            .insert(New whatsnew())
            .insert(New submit())
            .insert(New view())
            .insert(New catView())
            .insert(New user())
            .insert(New writerpage())
            .insert(New rss2())
            .insert(New rss())
            .insert(New random())
            .insert(New timelinepvp())
            .insert(New catpvp())
            .insert(New sitemap())
            .insert(New posterPreview(query.text, preview.pvClass, posterViewer.pvCssFile))
            .insert(New nameCacheProcessor("robots", network.pathSeperator + "robots.txt", secondsToTicks(60 * 60)))
            .insert(New internalFunctionCall())
            .insert(New homepage())
        End With
        ProHttpS.startServer(args, configNaming.wfileis.name, New httpContext.parameterDefinition(stringsPackage:=sp))
    End Sub

    Public Sub addwriter(ByVal context As httpContext, ByVal page As xhtmlpage, _
                         ByVal root As xmlcontrol, ByVal toviewpage As Boolean, _
                         Optional ByRef cs As String = Nothing)
        assert(Not context Is Nothing, "context is nothing.")
        assert(Not page Is Nothing, "page is nothing.")
        assert(Not root Is Nothing, "root is nothing.")

        Dim div As div = Nothing
        div = context.createControl(New div())
        colorfulControl.invoke(page, div)
        div.class = "wrtwrap"
        root.addchild(div)

        Dim label As label = Nothing
        label = context.createControl(New label())
        label.text = context.string(stringId.WIRTESOMETHINGTOKEY)
        div.addchild(label)

        div.addchild(context.createControl(New writer(New writer.datasource(page, context, toviewpage), cs)))
    End Sub

    Public Sub addvisitCounter(ByVal context As httpContext, ByVal root As xmlcontrol)
        assert(Not context Is Nothing, "context is nothing.")
        assert(Not root Is Nothing, "root is nothing.")

        Dim xmltext As xmltext = Nothing
        xmltext = context.createControl(New xmltext())
        xmltext.text = context.string(stringId.VISIT_COUNTER)
        root.addchild(xmltext)

        Dim a As a = Nothing
        a = context.createControl(New a())
        a.href = configDelegate.visitCounterHomepage
        root.addchild(a)

        Dim img As img = Nothing
        img = context.createControl(New img())
        img.src = configDelegate.visitCounterLink
        img.style = "border:0;display:block;"
        img.height = "24px"
        a.addchild(img)
    End Sub

    Public Sub separateCategories(ByVal cat As String, ByRef cats As vector(Of String))
        strsepar(cat, spaceChars, cats, ignoreEmptyEntity:=True)
    End Sub

    Public Sub separateCategories(ByVal context As httpContext, ByRef cats As vector(Of String))
        assert(Not context Is Nothing, "context is nothing.")
        separateCategories(context.url().query(query.category), cats)
    End Sub

    Public Function generateLink(ByVal context As httpContext, ByVal baseurl As String, ByVal ignoreQuery As String, _
                                 Optional ByVal ignoreText As Boolean = True) As String
        assert(Not context Is Nothing, "context is nothing.")

        Dim rtn As String = Nothing
        copy(rtn, baseurl)

        Dim it As map(Of String, String).iterator = Nothing
        it = context.url().query.begin()
        While it <> context.url().query.end()
            If Not strsame((+it).first, ignoreQuery, True) AndAlso _
               Not strsame((+it).first, query.nopage, True) AndAlso _
               Not strsame((+it).first, query.count, True) AndAlso _
               Not strsame((+it).first, query.first, True) AndAlso _
               Not strsame((+it).first, query.last, True) AndAlso _
               Not strsame((+it).first, query.toviewpage, True) AndAlso _
               Not strsame((+it).first, query.checksum, True) AndAlso _
               (Not ignoreText OrElse Not strsame((+it).first, query.text, True)) Then
                rtn = urlUtils.appendQuery(rtn, (+it).first, (+it).second)
            End If
            it += 1
        End While

        Return rtn
    End Function

    Public Delegate Sub processRecentTopicDelegate(ByVal key As String, _
                                                   ByVal record As record, ByVal infos As vector(Of pointer(Of info)), _
                                                   ByVal obj As Object)
    Public Delegate Sub processFailedTopicDelegate(ByVal key As String, ByVal obj As Object)
    Public Delegate Sub processNoContentDelegate(ByVal obj As Object)

    Public Sub insertInfo(ByVal infos As vector(Of pointer(Of info)), ByVal key As String)
        Const fakefail As Boolean = False
        assert(Not infos Is Nothing, "infos is nothing.")
        Dim info As info = Nothing
        info = New info()
        'fake some failed cases
        If fakefail AndAlso isdebugmode() AndAlso rnd(0, 1, False) > 0.9 Then
            infos.push_back(New pointer(Of info)(convertor.cast(Of info)(Nothing)))
        ElseIf info.read(key) Then
            infos.push_back(New pointer(Of info)(info))
        Else
            raiseError("cannot read info " + key, errorHandle.errorType.exclamation)
            infos.push_back(New pointer(Of info)(convertor.cast(Of info)(Nothing)))
        End If
    End Sub

    Public Function prepareInfos(ByVal r As record, ByRef output As vector(Of pointer(Of info))) As Boolean
        If debugassert(Not r Is Nothing) Then
            Dim recv As vector(Of String) = Nothing
            If list2vector(r, recv) AndAlso sort(recv.rawdata(), 0, recv.size()) AndAlso recv.size() > 0 Then
                If output Is Nothing Then
                    output = New vector(Of pointer(Of info))()
                Else
                    output.clear()
                End If
                insertInfo(output, recv(0))
                If recv.size() > 2 Then
                    insertInfo(output, recv(recv.size() - 2))
                End If
                If recv.size() > 1 Then
                    insertInfo(output, recv(recv.size() - 1))
                End If
                Return True
            Else
                raiseError(errorHandle.errorType.warning, "cannot list2vector or sort, or record is empty.")
                Return False
            End If
        Else
            Return False
        End If
    End Function

    Public Function prepareInfos(ByVal topic As String, ByRef record As record, _
                                 ByRef output As vector(Of pointer(Of info))) As Boolean
        record = New record()
        If Not record.read(topic) Then
            raiseError(errorHandle.errorType.warning, "cannot read topic ", topic)
            Return False
        Else
            Return prepareInfos(record, output)
        End If
    End Function

    Public Sub processRecentTopics(ByVal start As Int64, _
                                   ByVal recentTopicCount As Int64, _
                                   ByVal processRecentTopic As processRecentTopicDelegate, _
                                   ByVal processFailedTopic As processFailedTopicDelegate, _
                                   ByVal processNoContent As processNoContentDelegate, _
                                   ByVal obj As Object, ByRef nomore As Boolean)
        assert(Not processRecentTopic Is Nothing, "processRecentTopic callback is nothing.")
        assert(Not processFailedTopic Is Nothing, "processFailedTopic callback is nothing.")
        assert(Not processNoContent Is Nothing, "processNoContent callback is nothing.")

        Dim v As vector(Of recentElement) = Nothing
        v = wfileis.recentTopic.output()

        If Not v Is Nothing AndAlso v.size() > 0 AndAlso v.size() > start Then
            Dim i As Int64
            Dim j As Int64
#If RETIRED Then
            i = recentTopicCount - 1
            j = v.size() - start - 1
            Dim m As map(Of String, Int64) = Nothing
            m = New map(Of String, Int64)()
            While i >= 0 AndAlso j >= 0
                If m.find(v(j).name) = m.end() Then
                    i -= 1
                End If
                m(v(j).name) += 1
                j -= 1
            End While
#End If

            i = recentTopicCount - 1
            j = v.size() - start - 1
            Dim s As [set](Of String) = Nothing
            s = New [set](Of String)()
            While i >= 0 AndAlso j >= 0
                If s.find(v(j).name) = s.end() Then
#If RETIRED Then
                    Dim record As record = Nothing
                    record = New record()
                    Dim recv As vector(Of String) = Nothing
                    If record.read(v(j).name) AndAlso list2vector(record, recv) AndAlso _
                       sort(recv.rawdata(), 0, recv.size()) AndAlso recv.size() > 0 Then
                        Dim infos As vector(Of pointer(Of info)) = Nothing
                        infos = New vector(Of pointer(Of info))()
                        Dim info As info = Nothing
                        info = New info()
                        insertInfo(infos, recv(0))
                        'if the record contains more than 1 info, at least post the last info
                        'as recent will not record duplicate topic name + user name
                        If recv.size() > 1 Then
                            Dim k As Int64
                            For k = recv.size() - m(v(j).name) + 1 To recv.size() - 2
                                insertInfo(infos, recv(k))
                            Next
                            insertInfo(infos, recv(recv.size() - 1))
                        End If
                        processRecentTopic(v(j).name, record, infos, obj)
                        i -= 1
                    Else
                        raiseError("cannot read record " + v(j).name + ", or cannot list2vector, " + _
                                   "or cannot sort, or empty.", errorHandle.errorType.warning)
                        processFailedTopic(v(j).name, obj)
                    End If
#End If
                    Dim record As record = Nothing
                    Dim infos As vector(Of pointer(Of info)) = Nothing
                    If prepareInfos(v(j).name, record, infos) Then
                        processRecentTopic(v(j).name, record, infos, obj)
                        i -= 1
                    Else
                        raiseError(errorHandle.errorType.warning, "cannot process topic ", v(j).name)
                        processFailedTopic(v(j).name, obj)
                    End If
                    s.insert(v(j).name)
                End If
                j -= 1
            End While
            nomore = (i >= 0)
        Else
            nomore = True
            processNoContent(obj)
        End If
    End Sub

    Public Function getIdFromContext(ByVal context As httpContext, ByRef id As String) As Boolean
        Dim it As map(Of String, String).iterator = Nothing
        it = context.url().query.find(query.id)
        If it <> context.url().query.end() Then
            id = (+it).second
            Return True
        ElseIf Not context.cookie() Is Nothing Then
            it = context.cookie().find(query.id)
            If it <> context.cookie().end() Then
                copy(id, (+it).second)
                Return True
            End If
        End If

        Return False
    End Function

    Public Function getIdFromContext(ByRef id As String) As Boolean
        Return getIdFromContext(httpServer2.currentContext(), id)
    End Function
End Module

Public Class query
    Public Const key As String = "k"
    Public Const category As String = "c"
    Public Const id As String = "id"
    Public Const text As String = "t"
    Public Const first As String = "f"
    Public Const last As String = "l"
    Public Const toviewpage As String = "v"
    Public Const nopage As String = "n"
    Public Const recenttype As String = "r"
    Public Const count As String = "cnt"
    Public Const checksum As String = "cs"
End Class

Public Module common
    Public Const applicationName As String = "dressing ball"
End Module
